/****************************************************************************
**
****************************************************************************/

#include <QSerialPort>

#include "cardPuyue.h"

#include "QDebug"
#define CARD_DEBUG

static uint8_t s_anyue_encryption_key[6] = {0X01, 0X0E, 0X19, 0x23, 0X1A, 0X1B};

CardPuyue::CardPuyue(enum CardType cardType, QObject *parent) : AbstractCard(parent)
{
    card_state_ = AbstractCard::kCardIdle;
    reader_type_ = AbstractCard::KPuyue;
    card_type_ = cardType;
    card_number_ = "";
    card_num_len_ = 0;

    serialport_ = new QSerialPort;
    if (nullptr == serialport_) {
        return;
    }

#ifndef __arm__
    return;
#endif

    connect(serialport_, &QSerialPort::readyRead, this, &CardPuyue::HandleSerialData);

    serialport_->setPortName("/dev/ttyS3");
    serialport_->setBaudRate(115200);
    serialport_->setDataBits(QSerialPort::Data8);
    serialport_->setParity(QSerialPort::NoParity);
    serialport_->setStopBits(QSerialPort::OneStop);
    serialport_->setFlowControl(QSerialPort::NoFlowControl);

    if (serialport_->open(QIODevice::ReadWrite)) {
#if defined (CARD_DEBUG)
        qDebug() << "[PUYUE]-> card device open success";
#endif
    } else {
#if defined (CARD_DEBUG)
        qDebug() << "[PUYUE]-> card device open fail";
#endif
    }
}

CardPuyue::~CardPuyue()
{

}

void CardPuyue::TestSerial()
{

}

void CardPuyue::SearchCard()
{
    if (nullptr == serialport_) {
        return;
    }

    if (!serialport_->isOpen()) {
        return;
    }

    serialport_->write(Construct(kSearchLabel));
}

void CardPuyue::HandleSerialData()
{
    unsigned char frame_data[256] = {0};
    QByteArray    read_data;

    //接收完成的串口数据
    read_data = serialport_->readAll();
    while (serialport_->waitForReadyRead(10)) {
        read_data += serialport_->readAll();
    }

    //开始校验数据
    if (3 > read_data.count()) {
        set_card_state(AbstractCard::KCardOffline);
        return;
    }
    if ((read_data.at(0) != 0x7E) || (read_data.at(1) != 0x55)) {
        set_card_state(AbstractCard::KCardOffline);
        return;
    }
    int frameLen = read_data.at(2);
    if (read_data.count() < 2 + 1 + frameLen) {
        set_card_state(AbstractCard::KCardOffline);
        return;
    }
    for (int i = 2; i < read_data.count() - 2; i++) {
        frame_data[i - 2] = read_data.at(i);
    }
    unsigned int fcrc, ccrc;
    fcrc = (read_data.at(read_data.count() - 1) << 8) + read_data.at(read_data.count() - 2);
    ccrc = CalcCRC16(frame_data, read_data.count() - 2 - 2);
    if (fcrc != (ccrc & 0xFFFF)) {
        set_card_state(AbstractCard::KCardOffline);
        return;
    }

    //校验成功了，就开始处理数据
    QByteArray block_data;
    unsigned char cmd_code = read_data.at(8);
    switch (cmd_code) {
    case kSearchLabel:
        uid_.clear();
        uid_len_ = 0;
        for (int i = 0; i < 14; i++) {
            uid_.append(read_data.at(10 + i));
            uid_len_ = uid_len_ + 1;
        }
#if defined (CARD_DEBUG)
        qDebug() << "[PUYUE]-> uid data:" << uid_.toHex(' ').toUpper();
#endif
        serialport_->write(Construct(kReadM1DataBlock));
        break;
    case kReadM1DataBlock:
        if (0x00 != read_data.at(10 + 16)) {
            set_card_state(AbstractCard::KCardOffline);
            return;
        }
        for (int i = 0; i < 16; i++) {
            block_data.append(read_data.at(10 + i));
        }
#if defined (CARD_DEBUG)
        qDebug() << "[PUYUE]-> block data:" << block_data.toHex(' ').toUpper();
#endif
        switch (card_type_) {
        case kAnyue:
            if (4 > uid_.size()) {
                set_card_state(AbstractCard::KCardOffline);
                return;
            }
            card_number_.clear();
            card_num_len_ = 0;
            for (int i = 0; i < 12; i++) {
                card_number_.append((block_data.at(i) ^ uid_[i % 4]) - 0x30);
                card_num_len_ = card_num_len_ + 1;
            }

            emit HandleCardNumber(card_number_); // 获取到了卡号，发信号处理数据

            break;
        case kThaisen:
            break;
        case KYunkuaichong:
            break;
        case KThaisenAndYunkuaichong:
            break;
        default:
            break;
        }
        break;
    default:
        break;
    }
}

unsigned int CardPuyue::CalcCRC16(unsigned char *data, unsigned int len)
{
    unsigned int i = 0, j = 0, crc = 0xFFFF;

    for (i = 0; i < len; i++) {
        crc = crc ^ data[i];
        for (j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0x8408;
            } else {
                crc = (crc >> 1);
            }
        }
    }

    return (~crc);
}

QByteArray CardPuyue::Construct(CardPuyue::CardCmd cmd)
{
    unsigned int  crc = 0;
    unsigned char data[128] = {0};
    QByteArray    construct_data;

    switch (cmd) {
    case kSearchLabel:
        data[0] = 0X7E;
        data[1] = 0X55;
        data[2] = 0X08 + 0x01;

        data[3] = 0x00;
        data[4] = 0x00;
        data[5] = 0x01;
        data[6] = 0x00;
        data[7] = cmd;
        data[8] = 0x00;

        data[9] = 0X52;

        crc = CalcCRC16(&data[2], 8);
        data[10] = (crc  & 0xFF);
        data[11] = (crc >> 0x08) & 0xFF;

        construct_data.clear();
        for (int i = 0; i < 11 + 1; i++) {
            construct_data.append(data[i]);
        }
        break;
    case kReadM1DataBlock:
        data[0] = 0X7E;
        data[1] = 0X55;
        data[2] = 0X08 + 0x09;

        data[3] = 0x00;
        data[4] = 0x00;
        data[5] = 0x01;
        data[6] = 0x00;
        data[7] = cmd;
        data[8] = 0x00;

        switch (card_type_) {
        case kAnyue:
            data[ 9] = 0X60;
            data[10] = s_anyue_encryption_key[0]; //key start...
            data[11] = s_anyue_encryption_key[1];
            data[12] = s_anyue_encryption_key[2];
            data[13] = s_anyue_encryption_key[3];
            data[14] = s_anyue_encryption_key[4];
            data[15] = s_anyue_encryption_key[5]; //key end...
            break;
        case kThaisen:
            break;
        case KYunkuaichong:
            break;
        case KThaisenAndYunkuaichong:
            break;
        default:
            break;
        }

        data[16] = 0X06;
        data[17] = 0X01;

        crc = CalcCRC16(&data[2], 16);
        data[18] = (crc  & 0xFF);
        data[19] = (crc >> 0x08) & 0xFF;

        construct_data.clear();
        for (int i = 0; i < 19 + 1; i++) {
            construct_data.append(data[i]);
        }
        break;
    default:
        break;
    }

    return construct_data;
}
